from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response
from django.contrib.auth.decorators import login_required
from models import *
from twython import Twython
from forms import SearchForm
from geocode import *
from datetime import *
from dateutil.parser import *
from LineChart import *
import sys, re, itertools

CONSUMER_KEY = "F2jBEy7bzVqhSaXatxXOg"
CONSUMER_SECRET = "Jzaswra0adg3hIcUP9GnHqVH2IR86ZnR9DWHXHLu4nQ"
OAUTH_TOKEN = '221574457-DG5pE1k25XJacDpaZVAqQr7KrtZu7o58PxobsxYw'
OAUTH_TOKEN_SECRET = 'bOengS2AuhQoeeWKdL8vvP8auu6Fy6hZKMOxcC3SYg'

class SearchResultInfo(object):
	def __init__(self, hasResults, tweets, geo_coord_dict, related, all_tweets):
		self.hasResults = hasResults
		self.tweets = tweets
		self.geo_coord_dict = geo_coord_dict
		self.related = related
		self.all_tweets = all_tweets

class Tweet(object):
	def __init__(self, json):
		self.user = unescape(json['from_user'])
		self.text = unescape(json['text']).strip()
		self.location = json['location']
		self.time = parse(json['created_at'])
		self.geo = tuple(json['geo']['coordinates']) if json['geo'] and json['geo']['type']=="Point" else None
		self.state = None
	
	def location_to_ll(self):
		points = StringToLatLong.objects(string=self.location)
		return tuple(points[0].latLong) if points else None

def logout_user(request):
	logout(request)
	request.session.delete('twython')
	return HttpResponseRedirect('/')

def begin_auth(request):
	# Instantiate Twython with the first leg of our trip.
	twitter = Twython(
		twitter_token = CONSUMER_KEY,
		twitter_secret = CONSUMER_SECRET
	)
	
	# Request an authorization url to send the user to...
	auth_props = twitter.get_authentication_tokens()
	
	# Then send them over there
	request.session['request_token'] = auth_props
	request.session['post_login_url'] = request.path + '?' + request.META['QUERY_STRING']
	return HttpResponseRedirect(auth_props['auth_url'])

def twitter_comm(request):
	# Now that we've got the magic tokens back from Twitter, we need to exchange
	# for permanent ones and store them...
	
	if request.GET.has_key('denied'):
		HttpResponseRedirect('/')
	
	twitter = Twython(
		twitter_token = CONSUMER_KEY,
		twitter_secret = CONSUMER_SECRET,
		oauth_token = request.session['request_token']['oauth_token'],
		oauth_token_secret = request.session['request_token']['oauth_token_secret']
	)
	
	# Retrieve the tokens we want...
	authorized_tokens = twitter.get_authorized_tokens()
	
	# If they already exist, grab them, login and redirect to a page displaying stuff.
	try:
		user = User.objects.get(username = authorized_tokens['screen_name'])
	except User.DoesNotExist:
		# We mock a creation here; no email, password is just the token, etc.
		user = User.objects.create_user(authorized_tokens['screen_name'], "fake@email.com", authorized_tokens['oauth_token_secret'])
		profile = Profile()
		profile.user = user
		profile.oauth_token = authorized_tokens['oauth_token']
		profile.oauth_secret = authorized_tokens['oauth_token_secret']
		profile.save()
		
	user = authenticate(
		username = authorized_tokens['screen_name'],
		password = authorized_tokens['oauth_token_secret']
	)
	
	request.session['twython'] = twitter
	to_direct = request.session['post_login_url']
	request.session.delete('post_login_url')
	login(request, user)
	return HttpResponseRedirect(to_direct)

def unescape(s):
	s = s.replace('&lt;', '<')
	s = s.replace('&gt;', '>')
	s = s.replace('&amp;', '&')
	s = s.replace('&quot;', '"')
	return s

def index(request):	
	twitter = Twython()
	json = twitter.getCurrentTrends()
	dates = json['trends']
	
	trends = []
	for qlist in dates.values():
		for qdict in qlist:
			trends.append(qdict['query'])
	
	return render_to_response("index.html", {"trends":trends, "searchform": SearchForm()})

def search(request):
	#if not request.user.is_authenticated():
		#return begin_auth(request)
		
	if request.method != 'GET':
		return HttpResponseRedirect('/')
	q = request.GET.get('q', '').strip()
	
	if not q:
		return HttpResponseRedirect('/')		
	
	if not ',' in q:
		return singletopic(request, q)
	else:
		qs = q.split(',')
		return dualtopic(request, qs[0], qs[1])

def singletopic(request, q):	
	res = get_search_info(request, q, 5)
	return render_to_response("single.html", {
		"searchedfor":q.replace('%23','#'),
	 	"tweets":res.tweets,
		"hasResults":res.hasResults,
		"locations":res.geo_coord_dict,
		'related': res.related
		})

def dualtopic(request, q, q2):
	res1 = get_search_info(request, q, 5)
	res2 = get_search_info(request, q2, 5)
	tweets = res1.tweets[:3] + res2.tweets[:3]
	hasResults = res1.hasResults or res2.hasResults
	
	day_scores, sim_score = sim_scores(q, q2, res1.all_tweets, res2.all_tweets)
	chart_url = create_chart(day_scores)
	
	return render_to_response("double.html", {
		"searchedfor1":q.replace('%23','#'),
		"searchedfor2":q2.replace('%23','#'),
		"hasResults": hasResults,
		"tweets": tweets,
		"locations1":res1.geo_coord_dict,
		"locations2":res2.geo_coord_dict,
		'day_scores': day_scores,
		'total_score': sim_score * 100,
		'chart_url': chart_url,
		})

def get_search_info(request, topic, num_days):	
	topic = topic.replace('#', '%23')
	
	twitter = Twython(
	    twitter_token = CONSUMER_KEY,
	    twitter_secret = CONSUMER_SECRET,
	    oauth_token = OAUTH_TOKEN,
	    oauth_token_secret =  OAUTH_TOKEN_SECRET
    )
	
	cur_day = date.today()	
	all_tweets = []	# [ [Tweet] ]
	for i in range(num_days):
		tweets = []
		day_1 = (cur_day - timedelta(days=i)).isoformat()
		day_2 = (cur_day - timedelta(days=i+1)).isoformat()
		result = twitter.searchTwitter(q=topic, rpp="100", geocode="39.8079986572,-98.5546035767,1500mi", since =day_2, until=day_1)
		if not (result.has_key('error')):
			for tweet in result['results']:
				tweets.append(Tweet(tweet))
		all_tweets.append(tweets)
		
	geo_coords = [] # [ (lat,long) -> numtweets ]
	hasResults = False
	search_tokens = re.findall(r"\S+", topic)
	
	# check to see that at least one day has a result
	for day in all_tweets:
		if day:
			hasResults=True
			break
			
	if not hasResults:
		return SearchResultInfo(False, [], [[] for i in range(num_days)], [], [[] for i in range(num_days)])
	
	# each result_set is one day
	for day in all_tweets:
		lookup_locations = set()
		lookup_tweets = []	# [ Tweet ]
		geo_dict = {}
	
		for tweet in day:			
			geo = tweet.geo
			location = tweet.location
			if geo:	#  geo-tagged tweet
				if geo_dict.has_key(geo):
					geo_dict[geo] += 1
				else: 
					geo_dict[geo] = 1
				tweet.state = ll_to_state(geo)
			else:			
				if location:
					locInDB = StringToLatLong.objects(string=location)
					if locInDB:
						this_coord = tuple(locInDB[0].latLong)
						tweet.state = locInDB[0].state
						if geo_dict.has_key(this_coord):
							geo_dict[this_coord] += 1
						else: 
							geo_dict[this_coord] = 1
					else:
						lookup_tweets.append(tweet)
						lookup_locations.add(tweet.location)
		
		# all of the points that have valid latlongs are now in the database
		locations_to_ll(lookup_locations)
		
		for tweet in lookup_tweets:
			location = tweet.location_to_ll()
			if location:
				if geo_dict.has_key(location):
					geo_dict[location] += 1
				else: 
					geo_dict[location] = 1
		
		geo_coords.append(geo_dict.items())
	
	geo_coords.reverse()
	
	related = sim_topics(all_tweets, search_tokens, 5)
	
	return SearchResultInfo(hasResults, tweets[0:5], geo_coords, related, all_tweets)

def sim_topics(tweet_matrix, query, top_num):
	term_list = {}
	tweet_strings = [tweet.text.lower() for tweet in itertools.chain.from_iterable(tweet_matrix)]
	query = [word.lower() for word in query]
	for string in tweet_strings:
		string = re.sub(r'http://\S+','', string, flags=re.I)
		for term in re.findall(r"\w+",string):
			if term.lower() not in stopwords() and not term.isdigit() and term.lower() not in query:
				term_list[term] = term_list.get(term, 0) + 1
	
	sorted_list = sorted(term_list.iteritems(), key=lambda x: x[1], reverse=True)
	
	return [x[0] for x in sorted_list[:top_num]]

def sim_scores(term1, term2, all_tweets1, all_tweets2):
	day_scores = []
	for day in range(len(all_tweets1)):
		# state_scores = {}
		day_score = 0
		tweets1, tweets2 = all_tweets1[day], all_tweets2[day]
		if len(tweets1) + len(tweets2) == 0:
			day_scores.append(1) # totally similar
		for state in states():
			tweets1state = len([tweet for tweet in tweets1 if tweet.state == state])
			tweets2state = len([tweet for tweet in tweets2 if tweet.state == state])
			day_score += min(tweets1state, tweets2state)
		day_score /= float(len(tweets1) + len(tweets2))
		day_scores.append(day_score)
	score_avg = avg(day_scores)
	
	if CompareResult.objects(term1=term1, term2=term2):
		CR = CompareResult.objects(term1=term1, term2=term2)[0]
	else:
		CR = CompareResult(term1=term1, term2=term2)
	CR.days = day_scores
	CR.score = score_avg
	CR.save()
	
	print day_scores, score_avg
	return (day_scores, score_avg)

def min(a,b):
	return a if a<b else b


def max(a,b):
	return a if a>b else b

def avg(l):
	return sum(l)/float(len(l))

def stopwords():
	return ["a",
	"able","about","above","abst","accordance",
	"according","accordingly","across","act","actually",
	"added","adj","adopted","affected","affecting",
	"affects","after","afterwards","again","against",
	"ah","all","almost","alone","along",
	"already","also","although","always","am",
	"among","amongst","an","and","announce",
	"another","any","anybody","anyhow","anymore",
	"anyone","anything","anyway","anyways","anywhere",
	"apparently","approximately","are","aren","arent",
	"arise","around","as","aside","ask",
	"asking","at","auth","available","away",
	"awfully","b","back","be","became",
	"because","become","becomes","becoming","been",
	"before","beforehand","begin","beginning","beginnings",
	"begins","behind","being","believe","below",
	"beside","besides","between","beyond","biol",
	"both","brief","briefly","but","by",
	"c","ca","came","can","cannot",
	"can't","cause","causes","certain","certainly",
	"co","com","come","comes","contain",
	"containing","contains","could","couldnt","d",
	"date","did","didn't","different","do",
	"does","doesn't","doing","done","don't",
	"down","downwards","due","during","e",
	"each","ed","edu","effect","eg",
	"eight","eighty","either","else","elsewhere",
	"end","ending","enough","especially","et",
	"et","al","etc","even","ever",
	"every","everybody","everyone","everything","everywhere",
	"ex","except","f","far","few",
	"ff","fifth","first","five","fix",
	"followed","following","follows","for","former",
	"formerly","forth","found","four","from",
	"further","furthermore","g","gave","get",
	"gets","getting","give","given","gives",
	"giving","go","goes","gone","got",
	"gotten","h","had","happens","hardly",
	"has","hasn't","have","haven't","having",
	"he","hed","hence","her","here",
	"hereafter","hereby","herein","heres","hereupon",
	"hers","herself","hes","hi","hid",
	"him","himself","his","hither","home",
	"how","howbeit","however","hundred","i",
	"id","ie","if","i'll","im",
	"immediate","immediately","importance","important","in",
	"inc","indeed","index","information","instead",
	"into","invention","inward","is","isn't",
	"it","itd","it'll","its","itself",
	"i've","j","just","k","keep",
	"keeps","kept","keys","kg","km",
	"know","known","knows","l","largely",
	"last","lately","later","latter","latterly",
	"least","less","lest","let","lets",
	"like","liked","likely","line","little",
	"'ll","look","looking","looks","ltd",
	"m","made","mainly","make","makes",
	"many","may","maybe","me","mean",
	"means","meantime","meanwhile","merely","mg",
	"might","million","miss","ml","more",
	"moreover","most","mostly","mr","mrs",
	"much","mug","must","my","myself",
	"n","na","name","namely","nay",
	"nd","near","nearly","necessarily","necessary",
	"need","needs","neither","never","nevertheless",
	"new","next","nine","ninety","no",
	"nobody","non","none","nonetheless","noone",
	"nor","normally","nos","not","noted",
	"nothing","now","nowhere","o","obtain",
	"obtained","obviously","of","off","often",
	"oh","ok","okay","old","omitted",
	"on","once","one","ones","only",
	"onto","or","ord","other","others",
	"otherwise","ought","our","ours","ourselves",
	"out","outside","over","overall","owing",
	"own","p","page","pages","part",
	"particular","particularly","past","per","perhaps",
	"placed","please","plus","poorly","possible",
	"possibly","potentially","pp","predominantly","present",
	"previously","primarily","probably","promptly","proud",
	"provides","put","q","que","quickly",
	"quite","qv","r","ran","rather",
	"rd","re","readily","really","recent",
	"recently","ref","refs","regarding","regardless",
	"regards","related","relatively","research","respectively",
	"resulted","resulting","results","right","run",
	"s","said","same","saw","say",
	"saying","says","sec","section","see",
	"seeing","seem","seemed","seeming","seems",
	"seen","self","selves","sent","seven",
	"several","shall","she","shed","she'll",
	"shes","should","shouldn't","show","showed",
	"shown","showns","shows","significant","significantly",
	"similar","similarly","since","six","slightly",
	"so","some","somebody","somehow","someone",
	"somethan","something","sometime","sometimes","somewhat",
	"somewhere","soon","sorry","specifically","specified",
	"specify","specifying","state","states","still",
	"stop","strongly","sub","substantially","successfully",
	"such","sufficiently","suggest","sup","sure",
	"t","take","taken","taking","tell",
	"tends","th","than","thank","thanks",
	"thanx","that","that'll","thats","that've",
	"the","their","theirs","them","themselves",
	"then","thence","there","thereafter","thereby",
	"thered","therefore","therein","there'll","thereof",
	"therere","theres","thereto","thereupon","there've",
	"these","they","theyd","they'll","theyre",
	"they've","think","this","those","thou",
	"though","thoughh","thousand","throug","through",
	"throughout","thru","thus","til","tip",
	"to","together","too","took","toward",
	"towards","tried","tries","truly","try",
	"trying","ts","twice","two","u",
	"un","under","unfortunately","unless","unlike",
	"unlikely","until","unto","up","upon",
	"ups","us","use","used","useful",
	"usefully","usefulness","uses","using","usually",
	"v","value","various","'ve","very",
	"via","viz","vol","vols","vs",
	"w","want","wants","was","wasn't",
	"way","we","wed","welcome","we'll",
	"went","were","weren't","we've","what",
	"whatever","what'll","whats","when","whence",
	"whenever","where","whereafter","whereas","whereby",
	"wherein","wheres","whereupon","wherever","whether",
	"which","while","whim","whither","who",
	"whod","whoever","whole","who'll","whom",
	"whomever","whos","whose","why","widely",
	"willing","wish","with","within","without",
	"won't","words","world","would","wouldn't",
	"www","x","y","yes","yet",
	"you","youd","you'll","your","youre",
	"yours","yourself","yourselves","you've","z",
	"zero", "http", "rt"]
